package mdt.figure;

import mdt.program.Displayer;
import mdt.utils.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tigris.gef.base.Editor;
import org.tigris.gef.base.Globals;
import org.tigris.gef.presentation.Fig;
import org.tigris.gef.presentation.TextEditor;
import org.tigris.gef.properties.PropCategoryManager;
import org.tigris.gef.undo.UndoManager;

import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.ObjectOutputStream;


/**
 * This class handles painting and editing text Fig's in a LayerDiagram.
 * <p>
 * Within a text fig's outer dimensions,
 * there is a colored border with a certain width (0 or more),
 * an internal margin between the text and the border,
 * and the text area itself.
 * <p>
 * The internal margin and the text area are both colored in the text-fill color.
 * <p>
 * The fill-color inherited from the Fig class is used to draw the whole figure,
 * i.e. including the border. Hence it is only visible if either the border
 * or the text area are transparent.
 *
 * @author ics125 spring 1996
 */

public class FigTextMDT extends Fig implements KeyListener, MouseListener {

    private static final long serialVersionUID = 1L;

    public static final int IGNORE = 0;
    public static final int INSERT = 1;
    public static final int END_EDITING = 2;

    private int returnAction = IGNORE;
    private int tabAction = IGNORE;

    /** Constants to specify text justification. */
    public static final int JUSTIFY_LEFT = 0;
    public static final int JUSTIFY_RIGHT = 1;
    public static final int JUSTIFY_CENTER = 2;

    /** Minimum size of a FigText object. */
    public static final int MIN_TEXT_WIDTH = 30;

    /**
     * The internal representation of a return character.
     */
    private static final char HARD_RETURN = '\n';
    /**
     * The internal representation of a return due to word wrap.
     */
    private static final char SOFT_RETURN = '\r';

    /** Font info. */
    private Font _font = new Font("TimesRoman", Font.PLAIN, 10);
    private transient FontMetrics _fm;
    private int _lineHeight;

    /** Color of the actual text characters. */
    private Color textColor = Color.black;
    private Color borderColor = Color.black;
    private boolean bordreDashed = false;

    /** True if the text should be editable. False for read-only. */
    private boolean editable = true;

    private Class _textEditorClass = FigTextEditorMDT.class;
    /**
     * True if word wrap is to take place when editing multi-line text. False by
     * default (for backwards compatibility)
     */
    private boolean wordWrap = false;

    /** Extra spacing between lines. Default is 0 pixels. */
    private int _lineSpacing = 0;

    /** Internal margins between the text and the edge of the rectangle. */
    private int _topMargin = 0;
    private int _botMargin = 0;
    private int _leftMargin = 0;
    private int _rightMargin = 0;

    /** True if the FigText can only grow in size, never shrink. */
    private boolean _expandOnly = false;

    private boolean _editMode = false;

    /** Text justification can be JUSTIFY_LEFT, JUSTIFY_RIGHT, or JUSTIFY_CENTER. */
    private int _justification = JUSTIFY_LEFT;

    private StyledDocument document;

    private transient TextEditor textEditor;

    private static TextEditor activeTextEditor;

    private boolean enableArc = false;

    private transient Component awtComp;
    private transient JTextPane jtp;

    // //////////////////////////////////////////////////////////////
    // static initializer

    private static final Log LOG = LogFactory.getLog(org.tigris.gef.presentation.FigText.class);

    /**
     * This puts the text properties on the "Text" and "Style" pages of the
     * org.tigris.gef.ui.TabPropFrame.
     */
    static {
        PropCategoryManager.categorizeProperty("Text", "font");
        PropCategoryManager.categorizeProperty("Text", "underline");
        PropCategoryManager.categorizeProperty("Text", "expandOnly");
        PropCategoryManager.categorizeProperty("Text", "lineSpacing");
        PropCategoryManager.categorizeProperty("Text", "topMargin");
        PropCategoryManager.categorizeProperty("Text", "botMargin");
        PropCategoryManager.categorizeProperty("Text", "leftMargin");
        PropCategoryManager.categorizeProperty("Text", "rightMargin");
        PropCategoryManager.categorizeProperty("Text", "text");
        PropCategoryManager.categorizeProperty("Style", "justification");
        PropCategoryManager.categorizeProperty("Style", "textFilled");
        PropCategoryManager.categorizeProperty("Style", "textFillColor");
        PropCategoryManager.categorizeProperty("Style", "textColor");
    }

    // //////////////////////////////////////////////////////////////
    // constructors

    /**
     * Construct a new FigText with the given position, size, color, string,
     * font, and font size. Text string is initially empty and centered.
     */
    public FigTextMDT(int x, int y, int w, int h, Color textColor,
                   String familyName, int fontSize, boolean expandOnly) {
        super(x, y, Math.max(w, 80), Math.max(h, 30));
        if (w < 80) w = 80;
        if (h < 30) h = 30;
        _x = x;
        _y = y;
        _w = w;
        _h = h;
        this.textColor = textColor;
        _font = new Font(familyName, Font.PLAIN, fontSize);
        _justification = JUSTIFY_CENTER;
        document = new DefaultStyledDocument();
        _expandOnly = expandOnly;

        ensureJTP();

    /* This makes the text not touch the text-border line: */
        setTopMargin(1);
        setBotMargin(1);
        setLeftMargin(1);
        setRightMargin(1);

    }



    public FigTextMDT(int x, int y, int w, int h, Color textColor,
                   String familyName, int fontSize) {
        this(x, y, w, h, textColor, familyName, fontSize, false);
    }

    public FigTextMDT(int x, int y, int w, int h, Color textColor, Font font) {
        this(x, y, w, h, textColor, font.getName(), font.getSize());
    }

    /** Construct a new FigText with the given position and size */
    public FigTextMDT(int x, int y, int w, int h) {
        this(x, y, w, h, null, null, 0, false);
    }

    /** Construct a new FigText with the given position, size, and attributes. */
    public FigTextMDT(int x, int y, int w, int h, boolean expandOnly) {
        this(x, y, w, h, null, null, 0, expandOnly);
    }

    // //////////////////////////////////////////////////////////////
    // accessors

    /**
     * Reply a string that indicates how the text is justified: Left, Center, or
     * Right.
     */
    public String getJustificationByName() {
        if (_justification == JUSTIFY_LEFT)
            return "Left";
        else if (_justification == JUSTIFY_CENTER)
            return "Center";
        else if (_justification == JUSTIFY_RIGHT)
            return "Right";
        LOG.error("internal error, unknown text alignment");
        return "Unknown";
    }

    /**
     * Set the text justification given one of these strings: Left, Center, or
     * Right.
     */
    public void setJustificationByName(String justifyString) {
        int justification = -1;
        if (justifyString.equals("Left"))
            justification = JUSTIFY_LEFT;
        else if (justifyString.equals("Center"))
            justification = JUSTIFY_CENTER;
        else if (justifyString.equals("Right"))
            justification = JUSTIFY_RIGHT;
        _fm = null;
        setJustification(justification);
    }

    // //////////////////////////////////////////////////////////////
    // accessors and modifiers


    public Color getTextColor() {
        return textColor;
    }

    public void setTextColor(Color c) {
        firePropChange("textColor", textColor, c);
        textColor = c;
    }

    public void setBorderColor(Color c) {
        firePropChange("borderColor", borderColor, c);
        borderColor = c;
        updateBorder();
    }

    public void setBorderDashed(boolean dashed) {
        firePropChange("borderDashed", bordreDashed, dashed);
        bordreDashed = dashed;
        updateBorder();
    }

    public Color getBorderColor() {
        return borderColor;
    }

    public boolean getEditable() {
        return editable;
    }

    public void setEditable(boolean e) {
        firePropChange("editable", editable, e);
        editable = e;
    }

    public int getJustification() {
        return _justification;
    }

    public void setJustification(int align) {
        if (_justification == align) {
            return;
        }
        firePropChange("justification", getJustification(), align);
        _justification = align;
        setJTPJustification();
    }


    private void setJTPJustification() {
        if (jtp != null) {
            SimpleAttributeSet attribs = new SimpleAttributeSet();
            switch (_justification) {
                case JUSTIFY_CENTER:
                    StyleConstants.setAlignment(attribs, StyleConstants.ALIGN_CENTER);
                    break;
                case JUSTIFY_LEFT:
                    StyleConstants.setAlignment(attribs, StyleConstants.ALIGN_LEFT);
                    break;
                case JUSTIFY_RIGHT:
                    StyleConstants.setAlignment(attribs, StyleConstants.ALIGN_RIGHT);
                    break;
            }

            jtp.setParagraphAttributes(attribs, true);
        }
    }

    public int getLineSpacing() {
        return _lineSpacing;
    }

    public void setLineSpacing(int s) {
        firePropChange("lineSpacing", _lineSpacing, s);
        _lineSpacing = s;
        calcBounds();
    }

    public void setText(String text) {
        if (text == null) {
            text = "";
        }
        try {
            document.remove(0, document.getLength());
            document.insertString(0, text, null);
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    public String getText() {
        try {
            return document.getText(0, document.getLength());
        } catch (BadLocationException e) {
            e.printStackTrace();
            return null;
        }
    }

    public int getTopMargin() {
        return _topMargin;
    }

    public void setTopMargin(int m) {
        firePropChange("topMargin", _topMargin, m);
        _topMargin = m;
        updateBorder();
        calcBounds();
    }

    public int getBotMargin() {
        return _botMargin;
    }

    public void setBotMargin(int m) {
        firePropChange("botMargin", _botMargin, m);
        _botMargin = m;
        updateBorder();
        calcBounds();
    }

    public int getLeftMargin() {
        return _leftMargin;
    }

    public void setLeftMargin(int m) {
        firePropChange("leftMargin", _leftMargin, m);
        _leftMargin = m;
        updateBorder();
        calcBounds();
    }

    public int getRightMargin() {
        return _rightMargin;
    }

    public void setRightMargin(int m) {
        firePropChange("rightMargin", _rightMargin, m);
        _rightMargin = m;
        updateBorder();
        calcBounds();
    }

    public boolean getExpandOnly() {
        return _expandOnly;
    }

    public void setExpandOnly(boolean b) {
        firePropChange("expandOnly", _expandOnly, b);
        _expandOnly = b;
    }

    public Font getFont() {
        return _font;
    }

    public void setFont(Font f) {
        firePropChange("font", _font, f);
        _font = f;
        _fm = null;
        calcBounds();
    }

    /**
     * USED BY PGML.tee
     */
    public String getFontFamily() {
        return _font.getFamily();
    }

    /**
     * USED BY PGML.tee
     */
    public void setFontFamily(String familyName) {
        Font f = new Font(familyName, _font.getStyle(), _font.getSize());
        setFont(f);
    }

    /**
     * USED BY PGML.tee
     */
    public int getFontSize() {
        return _font.getSize();
    }

    /**
     * USED BY PGML.tee
     */
    public void setFontSize(int size) {
        Font f = new Font(_font.getFamily(), _font.getStyle(), size);
        setFont(f);
    }

    public boolean getItalic() {
        return _font.isItalic();
    }

    public void setItalic(boolean b) {
        if (getItalic() == b) {
            return;
        }
        int style = (getBold() ? Font.BOLD : 0) + (b ? Font.ITALIC : 0);
        Font f = new Font(_font.getFamily(), style, _font.getSize());
        setFont(f);
    }

    public boolean getBold() {
        return _font.isBold();
    }

    public void setBold(boolean b) {
        if (getBold() == b) {
            return;
        }
        int style = (b ? Font.BOLD : 0) + (getItalic() ? Font.ITALIC : 0);
        setFont(new Font(_font.getFamily(), style, _font.getSize()));
    }

    /**
     * @deprecated see setReturnAction and setLineSeparator
     * @param b
     */
    public void setMultiLine(boolean b) {
        if (b) {
            returnAction = INSERT;
        } else {
            returnAction = END_EDITING;
        }
    }

    /**
     * @deprecated use getReturnAction()
     * @return true if multiple lines of text can be entered.
     */
    public boolean getMultiLine() {
        return returnAction == INSERT;
    }

    public boolean isWordWrap() {
        return wordWrap;
    }

    /**
     * Specifies what action the control should take on return press.
     *
     * @param action
     *                values are IGNORE, INSERT or END_EDITING
     */
    public void setReturnAction(int action) {
        returnAction = action;
    }

    /**
     * Specifies what action the control should take on tab press.
     *
     * @param action
     *                values are IGNORE, INSERT or END_EDITING
     */
    public void setTabAction(int action) {
        tabAction = action;
    }

    /**
     * Discover what action the control will take on tab press.
     *
     * @return IGNORE, INSERT or END_EDITING
     */
    public int getTabAction() {
        return tabAction;
    }

    /**
     * Discover what action the control will take on return press.
     *
     * @return IGNORE, INSERT or END_EDITING
     */
    public int getReturnAction() {
        return returnAction;
    }

    /**
     * Allow tab presses to be inserted into text during edit
     *
     * @param b
     *                true if return presses are insertable
     * @deprecated use setTabAction(...)
     */
    public void setAllowsTab(boolean b) {
        if (b) {
            tabAction = INSERT;
        } else {
            tabAction = END_EDITING;
        }
    }

    /**
     * Turn on/off word wrapping of text.
     *
     * @param b
     *                true if word wrap is to take place
     */
    public void setWordWrap(boolean b) {
        wordWrap = b;
    }

    /**
     * @deprecated use getTabAction()
     */
    public boolean getAllowsTab() {
        return tabAction == INSERT;
    }


    /**
     * Determine the owner of the given Fig by recursing up through groups until
     * an owner is found
     */
    private Object getOwner(Fig fig) {
        Object owner = fig.getOwner();
        if (owner != null) {
            return owner;
        }
        Fig figGroup = fig.getGroup();
        if (figGroup == null) {
            return null;
        } else {
            return getOwner(figGroup);
        }
    }

    /**
     * @deprecated in 0.11.1 it appears that the editor must always be
     *             FigTextEditor
     */
    public Class getTextEditorClass() {
        return _textEditorClass;
    }

    /**
     * @deprecated in 0.11.1 it appears that the editor must always be
     *             FigTextEditor
     */
    public void setTextEditorClass(Class editorClass) {
        _textEditorClass = editorClass;
    }

    /**
     * Paint the FigText.
     * This comprises <ul>
     * <li>the color of the whole box of the Fig if it is filled,
     * <li>the border colour (which has a linewidth of 1 or more),
     * <li>the text background colour if textfill is true,
     * <li>and the text itself.
     * </ul>
     */
    public void paintTextComponent(Graphics g) {
        if (!(isVisible())) {
            return;
        }

        if (isFilled() && fillType != FILL_TYPE.SOLID) { // assume JTP opaque is already set to false
            Graphics2D g2 = (Graphics2D) g;
            Paint oldPaint = g2.getPaint();
            Paint paint = getDefaultPaint();
            if (paint != null) {
                g2.setPaint(paint);
                drawRect(g2,
                        true,
                        fillType,
                        getFillColor(),
                        getGradientStart(),
                        getGradientEnd(),
                        getLineWidth(),
                        getLineColor(),
                        _x, _y, _w, _h,
                        getDashed(),
                        _dashes,
                        _dashPeriod);
            }
            g2.setPaint(oldPaint);
        }

        ensureJTP();

        Shape clip = g.getClip();
        g.translate(_x, _y);
        g.setClip(0, 0, _w, _h);
        jtp.paint(g);
        g.translate(-_x, -_y);
        g.setClip(clip);
    }

    protected void translateImpl(int dx, int dy) {
        super.translateImpl(dx, dy);
        ensureJTP();
        jtp.setBounds(_x, _y, _w, _h);
    }

    public void setLineColor(Color color) {
        setBorderColor(color);
        super.setLineColor(color);
    }

    public void setDashed(boolean dashed) {
        setBorderDashed(dashed);
        super.setDashed(dashed);
    }

    public void appendSvg(StringBuffer sb) {
        sb.append("  <rect id='").append(getId()).append("' x='").append(getX())
                .append("' y='").append(getY()).append("' width='").append(
                getWidth()).append("' height='").append(getHeight())
                .append("'");
        if (enableArc) {
            sb.append(" rx='" + getArcLength() + "' ry='" + getArcLength() + "'");
        }
        appendSvgStyle(sb);
        sb.append(" />");

        StringBuffer textSb = new StringBuffer();
        StringBuffer bgRectSb = new StringBuffer();

        ensureJTP();
        Element rootElement = document.getDefaultRootElement();
        Element paragraphElement, textElement;
        int fieldIdx = 0;
        for (int lpParagraph = 0; lpParagraph < rootElement.getElementCount(); lpParagraph++) {
            paragraphElement = rootElement.getElement(lpParagraph);

            int paraStart = paragraphElement.getStartOffset();
            int paraEnd = paragraphElement.getEndOffset();

            StringBuffer currBuffer = null;
            for (int lpText = 0; lpText < paragraphElement.getElementCount(); lpText++) {
                try {
                    textElement = paragraphElement.getElement(lpText);
                    int start = textElement.getStartOffset();
                    int end = textElement.getEndOffset();
                    AttributeSet as = textElement.getAttributes();
                    String family = StyleConstants.getFontFamily(as);
                    int size = StyleConstants.getFontSize(as);
                    Color color = StyleConstants.getForeground(as);
                    Color bgcolor = StyleConstants.getBackground(as);
                    boolean isBold = StyleConstants.isBold(as);
                    boolean isItalic = StyleConstants.isItalic(as);
                    boolean isUnderline = StyleConstants.isUnderline(as);
                    Font font = new Font(family, (isBold ? Font.BOLD : Font.PLAIN) | (isItalic ? Font.ITALIC : 0), size);
                    Graphics graphics = Displayer.instance().getJFrame().getGraphics();
                    FontMetrics metrics = graphics.getFontMetrics(font);

                    java.util.List<Integer> offsets = getElementLineOffsets(start, end);

                    for (int i = 0; i < offsets.size() - 1; i++){
                        String content = document.getText(offsets.get(i), offsets.get(i+1) - offsets.get(i));
                        content = content.replaceAll("\n", "");
                        if (content.length() == 0) continue;

                        currBuffer = new StringBuffer();
                        Rectangle rect = jtp.modelToView(offsets.get(i));
                        currBuffer.append("  <text id ='").append(getId() + "." + fieldIdx++).append("'")
                                .append(" x='" + (_x + rect.x) + "'")
                                .append(" y='" + (_y + rect.y + rect.height) + "'")
                                .append(" xml:space='preserve'")
                                .append(" fill='rgb(" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ")'")
                                .append(" textLength='").append(metrics.stringWidth(content)).append("'")
                                .append(" font-family='" + family + "'")
                                .append(" font-size='" + size + "'")
                                .append(" font-weight='" + (isBold ? "bold" : "normal") + "'")
                                .append(" font-style='" + (isItalic ? "italic" : "normal") + "'")
                                .append(" text-decoration='" + (isUnderline ? "underline" : "none") + "'")
                                .append(">");
                        currBuffer.append(StringUtils.encodeXmlString(content)).append("</text>\n");
                        textSb.append(currBuffer);

                        if (bgcolor != null && !bgcolor.equals(Color.black)) {
                            bgRectSb.append("<rect x='" + (_x + rect.x) + "' y='" + (_y + rect.y + rect.height - metrics.getHeight() + metrics.getDescent())
                                    + "' width='" + metrics.stringWidth(content) + "' height='" + metrics.getHeight()
                                    + "' style='fill-opacity:1.0;fill:" + getSVGColor(bgcolor)
                                    + ";stroke-width:0;stroke:rgb(0,0,0);' />");
                        }
                    }
                } catch (Exception e) {
                    System.err.println("Discarded text: " + currBuffer);
                }

            }
        }

        if (bgRectSb.length() > 0) {
            sb.append(bgRectSb);
        }
        sb.append(textSb);

    }

    private java.util.List<Integer> getElementLineOffsets(int start, int end) {
        java.util.List<Integer> list = new java.util.ArrayList<>();
        try {
            String text = document.getText(start, end - start);
            do {
                Rectangle startRect = jtp.modelToView(start);
                Rectangle endRect = jtp.modelToView(end - 1);
                if (startRect.y == endRect.y) {
                    list.add(start);
                    list.add(end);
                    break;
                }
                list.add(start);
                int y = -1;
                for (int i = start; i < end; i++) {
                    Rectangle testRect = jtp.modelToView(i);
                    if (y == -1) {
                        y = testRect.y;
                    } else {
                        if (y == testRect.y) {
                            continue;
                        } else {
                            y = testRect.y;
                            list.add(i);
                        }
                    }
                }
                list.add(end);
                break;
            } while (true);
        } catch (BadLocationException e) {
            list.clear();
            list.add(start);
            list.add(end);
        }

        return list;
    }

    /**
     * Mouse clicks are handled differently that the default Fig behavior so
     * that it is easier to select text that is not filled. Needs-More-Work:
     * should actually check the individual text rectangles.
     */
    public boolean hit(Rectangle r) {
        int cornersHit = countCornersContained(r.x, r.y, r.width, r.height);
        return cornersHit > 0;
    }

    /**
     * @return the distance between the baseline of adjacent lines of text
     */
    public int getMinimumHeight() {
        ensureJTP();
        return jtp.getMinimumSize().height;
    }

    public Dimension getMinimumSize() {
        ensureJTP();
        return jtp.getMinimumSize();
    }


    // //////////////////////////////////////////////////////////////
    // event handlers: KeyListener implementation

    /**
     * When the user presses a key when a FigText is selected, that key should
     * be added to the current string and we start editing.
     */
    public void keyTyped(KeyEvent ke) {
        // This code must be in keyTyped rather than keyPressed.
        // If in keyPressed some platforms will automatically add the pressed
        // key to the editor when it opens others do not.
        // Using keyTyped it is not automatically added and we do so ourselves
        // if it is not some control character.
        if (isStartEditingKey(ke) && editable) {
            ke.consume();
            TextEditor te = startTextEditor(ke);
            if (!Character.isISOControl(ke.getKeyChar())) {
                te.setText(te.getText() + ke.getKeyChar());
            }
        }
    }

    public void keyPressed(KeyEvent ke) {
    }

    public void keyReleased(KeyEvent ke) {
    }

    protected boolean isStartEditingKey(KeyEvent ke) {
        return (!Character.isISOControl(ke.getKeyChar()));
    }

    // //////////////////////////////////////////////////////////////
    // event handlers: KeyListener implemtation

    public void mouseClicked(MouseEvent me) {
        if (me.isConsumed())
            return;
        if (me.getClickCount() >= 2 && editable) {
            startTextEditor(me);
            me.consume();
        }
    }

    public void mousePressed(MouseEvent me) {
    }

    public void mouseReleased(MouseEvent me) {
    }

    public void mouseEntered(MouseEvent me) {
    }

    public void mouseExited(MouseEvent me) {
    }

    public TextEditor startTextEditor(InputEvent ie) {
        ensureJTP();
        jtp.setVisible(false);
        textEditor = new FigTextEditorMDT(this, ie);
        activeTextEditor = textEditor;
        _editMode = true;
        return textEditor;
    }



    // //////////////////////////////////////////////////////////////
    // internal utility functions

    /**
     * Compute the overall width and height of the FigText object based on the
     * font, font size, and current text. Needs-More-Work: Right now text
     * objects can get larger when you type more, but they do not get smaller
     * when you backspace.
     */
    public void calcBounds() { //TODO
        ensureJTP();
        _w = Math.max(_w, jtp.getMinimumSize().width);
        _h = Math.max(_h, jtp.getMinimumSize().height);
    }



    private boolean isSoftReturn(String text) {
        return (text.length() == 1 && text.charAt(0) == SOFT_RETURN);
    }

    private boolean isHardReturn(String text) {
        return (text.length() == 1 && text.charAt(0) == HARD_RETURN);
    }

    private boolean isSpace(String text) {
        return (text.length() == 1 && text.charAt(0) == ' ');
    }

    //private JPanel jc;
    private JTextPane ensureJTP() {
        if (jtp != null) return jtp;

        /*
        jc = new JPanel() {
            public void paintComponent(Graphics og) {
                if (isFilled() && fillType != FILL_TYPE.SOLID) { // assume opaque is already set to false
                    Graphics2D g2 = (Graphics2D) og;
                    Paint oldPaint = g2.getPaint();
                    Paint paint = getDefaultPaint();
                    if (paint != null) {
                        g2.setPaint(paint);
                        FigTextMDT.this.drawRect(
                                g2,
                                fillType,
                                getFillColor(),
                                getGradientStart(),
                                getGradientEnd(),
                                getLineWidth(),
                                getLineColor(),
                                _x, _y, _w, _h,
                                getDashed(),
                                _dashes,
                                _dashPeriod);
                    }
                    g2.setPaint(oldPaint);
                }
            }
        };
        jc.setLayout(new BorderLayout());
        jc.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        */

        jtp = new JTextPane(document);
        //jc.add(jtp, BorderLayout.CENTER);
        jtp.setSize(_w, _h);
        jtp.setEditable(false);
        jtp.setVisible(false);
        jtp.setBackground(getFillColor());
        jtp.setOpaque(_filled && fillType == FILL_TYPE.SOLID);
        updateBorder();
        setJTPJustification();

        Editor ce = Globals.curEditor();
        JComponent drawingPanel = (JPanel) ce.getJComponent();
        UndoManager.getInstance().startChain();
        // walk up and add to glass pane
        awtComp = drawingPanel;
        while (!(awtComp instanceof RootPaneContainer) && awtComp != null) {
            awtComp = awtComp.getParent();
        }

        return jtp;
    }

    private void writeObject(ObjectOutputStream oos) {
        document = copyDocument(document, 0, document.getLength());
        jtp = null;
        try {
            oos.defaultWriteObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void setLineWidth(int w) {
        super.setLineWidth(w);
        updateBorder();
    }

    public void setFillColor(Color c) {
        if (jtp != null) {
            jtp.setBackground(c);
        }
        super.setFillColor(c);
    }

    public void setFilled(boolean filled) {
        if (filled) {
            if (fillType == FILL_TYPE.SOLID) {
                ensureJTP().setOpaque(true);
            } else {
                ensureJTP().setOpaque(false);
            }
        } else {
            ensureJTP().setOpaque(false);
        }
        super.setFilled(filled);
    }

    public void setFillType(FILL_TYPE fillType) {
        if (isFilled()) {
            if (fillType == FILL_TYPE.SOLID) {
                ensureJTP().setOpaque(true);
            } else {
                ensureJTP().setOpaque(false);
            }
        }
        super.setFillType(fillType);
    }

    public StyledDocument getDocument() {
        return document;
    }

    public boolean isEnableArc() {
        return enableArc;
    }

    public void setEnableArc(boolean enableArc) {
        if (this.enableArc == enableArc) return;
        this.enableArc = enableArc;
        updateBorder();
    }

    protected int getArcLength() {
        return 12;
    }

    public void paint(Graphics g) {
        int lineWidth = getLineWidth();
        if (enableArc && lineWidth > 0) {
            final int arcLength = getArcLength();
            if (isFilled()) {
                fillRect(g, _x, _y, _w, _h, arcLength, arcLength);
                setFilled(false);
                setLineWidth(0);
                paintTextComponent(g);
                setFilled(true);
                setLineWidth(lineWidth);
                drawRectline(g, _x, _y, _w, _h, arcLength, arcLength);
            } else {
                paintTextComponent(g);
                drawRectline(g, _x, _y, _w, _h, arcLength, arcLength);
            }
        } else {
            paintTextComponent(g);
        }
    }

    private void updateBorder() {
        if (jtp == null) return;

        if (borderColor != null && getLineWidth() > 0) {
            jtp.setBorder(BorderFactory.createCompoundBorder(
                    (bordreDashed ? BorderFactory.createDashedBorder(borderColor, getLineWidth(), 5.0f, 5.0f, false)
                        :BorderFactory.createLineBorder(borderColor, getLineWidth())),
                    BorderFactory.createEmptyBorder(_topMargin, _leftMargin, _botMargin, _rightMargin)));
        } else {
            jtp.setBorder(BorderFactory.createEmptyBorder(_topMargin, _leftMargin, _botMargin, _rightMargin));
        }
    }

    protected void setBoundsImpl(int x, int y, int w, int h) {

        ensureJTP();
        Rectangle bbox = new Rectangle(x, y, w, h);
        bbox = SwingUtilities.convertRectangle(Globals.curEditor().getJComponent(), bbox, ((RootPaneContainer) awtComp).getLayeredPane());

        // bounds will be overwritten later in updateFigText anyway...
        jtp.setBounds(bbox.x, bbox.y, bbox.width, bbox.height);
        super.setBoundsImpl(x, y, w, h);
    }

    public void updateBounds() {
        Rectangle bbox = getBounds();
        Dimension minSize = getMinimumSize();
        bbox.width = Math.max(bbox.width, minSize.width);
        bbox.height = Math.max(bbox.height, minSize.height);
        setBounds(bbox.x, bbox.y, bbox.width, bbox.height);
    }

    public Object clone() {
        FigTextMDT cloned = (FigTextMDT)super.clone();

        cloned.document = copyDocument(document, 0, document.getLength());
        cloned.jtp = null;
        cloned.awtComp = null;
        cloned.textEditor = null;
        return cloned;
    }


    public static DefaultStyledDocument copyDocument(StyledDocument src, int selectionStart, int selectionEnd) {
        Element rootElement, paragraphElement, textElement;
        SimpleAttributeSet copyAttrs;
        int startOffset, endOffset;
        String copy_string;

        rootElement = src.getDefaultRootElement();
        DefaultStyledDocument copyDoc = new DefaultStyledDocument();

        for (int lpParagraph = 0; lpParagraph < rootElement.getElementCount(); lpParagraph++) {
            paragraphElement = rootElement.getElement(lpParagraph);

            //Check if the paragraph need to be copy
            if (paragraphElement.getEndOffset() < selectionStart) {
                continue; //Go to the next paragraph
            }
            if (paragraphElement.getStartOffset() > selectionEnd) {
                break; //Exit the boucle
            }

            for (int lpText = 0; lpText < paragraphElement.getElementCount(); lpText++) {
                //Insert a Element in the new Document
                textElement = paragraphElement.getElement(lpText);

                //Check if the Element need to be copy
                if (textElement.getEndOffset() < selectionStart) {
                    continue; //Go to the next Element
                }
                if (textElement.getStartOffset() > selectionEnd) {
                    break; //Exit the boucle
                }

                copyAttrs = new SimpleAttributeSet(textElement.getAttributes());

                //Find the value of startOffset and endOffset
                if (textElement.getStartOffset() < selectionStart) {
                    startOffset = selectionStart;
                } else {
                    startOffset = textElement.getStartOffset();
                }
                if (textElement.getEndOffset() > selectionEnd) {
                    endOffset = selectionEnd;
                } else {
                    endOffset = textElement.getEndOffset();
                }

                try {
                    copy_string = src.getText(startOffset, (endOffset - startOffset));
                    copyDoc.insertString(copyDoc.getLength(), copy_string, copyAttrs);
                } catch (BadLocationException e) {
                    System.err.println("Pogner une exception");
                }
            }
            //Modify the Style of the paragraph
            copyAttrs = new SimpleAttributeSet(paragraphElement.getAttributes());
            startOffset = paragraphElement.getStartOffset();
            endOffset = paragraphElement.getEndOffset();

            copyDoc.setParagraphAttributes(startOffset, (endOffset - startOffset), copyAttrs, true);
        }

        return copyDoc;
    }
} /* end class FigText */
